﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using NetMicro.Core.ConfigureOptions;
using NetMicro.Core.Ioc.ServiceLocation;
using NetMicro.Webs.DynamicWebApiController.Options;

namespace NetMicro.Core.Helper
{
    /// <summary>
    /// 启动全局对象
    /// </summary>
    public static class App
    {
        /// <summary>
        ///  获取泛型主机环境
        /// </summary>
        public static IHostEnvironment HostEnvironment => _hostEnvironment;

        /// <summary>
        /// 全局配置选项
        /// </summary>
        public static IConfiguration Configuration => _configuration;


        /// <summary>
        /// 全局配置选项
        /// </summary>
        private static IConfiguration _configuration;

        /// <summary>
        /// 添加配置选项
        /// </summary>
        /// <param name="configuration"></param>
        public static void SetConfiguration(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        /// <summary>
        ///  获取泛型主机环境
        /// </summary>
        private static IHostEnvironment _hostEnvironment;
        /// <summary>
        /// 添加泛型主机环境
        /// </summary>
        /// <param name="hostEnvironment"></param>
        public static void SetHostEnvironment(IHostEnvironment hostEnvironment)
        {
            _hostEnvironment = hostEnvironment;
        }


        /// <summary>
        ///模块化配置
        /// </summary>
        public static ModuleSettingsOptions Settings() => GetConfig<ModuleSettingsOptions>("ModuleSettings", true);

        /// <summary>
        /// 应用有效程序集
        /// </summary>
        public static IEnumerable<Assembly> Assemblies
        {
            get
            {
                return GetAssemblies().Assemblies;
            }
        }

        /// <summary>
        /// 外部程序集
        /// </summary>
        public static IEnumerable<Assembly> ExternalAssemblies
        {
            get
            {
                return GetAssemblies().ExternalAssemblies;
            }
        }

        /// <summary>
        /// 获取应用有效程序集
        /// </summary>
        /// <returns>IEnumerable</returns>
        private static (IEnumerable<Assembly> Assemblies, IEnumerable<Assembly> ExternalAssemblies) GetAssemblies()
        {
            // 读取应用配置
            string[] supportPackageNamePrefixs = Settings().SupportPackageNamePrefixs ?? Array.Empty<string>();
            DependencyContext dependencyContext = DependencyContext.Default;
            // 读取项目程序集 NUGET发布的包，或手动添加引用的dll，或配置特定的包前缀
            IEnumerable<Assembly> scanAssemblies = dependencyContext.RuntimeLibraries
                .Where(u =>
                       (u.Type == "project") ||
                       (u.Type == "package" && (u.Name.StartsWith("NetMicro") || supportPackageNamePrefixs.Any(p => u.Name.StartsWith(p)))) ||
                       (Settings().EnabledReferenceAssemblyScan == true && u.Type == "reference"))    // 判断是否启用引用程序集扫描
                .Select(u => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(u.Name)));

            IEnumerable<Assembly> externalAssemblies = Array.Empty<Assembly>();

            // 加载 `appsetting.json` 配置的外部程序集
            if (Settings().ExternalAssemblies != null && Settings().ExternalAssemblies.Any())
            {
                foreach (string externalAssembly in Settings().ExternalAssemblies)
                {
                    // 加载外部程序集
                    string assemblyFileFullPath = Path.Combine(AppContext.BaseDirectory
                        , externalAssembly.EndsWith(".dll") ? externalAssembly : $"{externalAssembly}.dll");

                    // 根据路径加载程序集
                    Assembly loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFileFullPath);
                    if (loadedAssembly == default) continue;
                    Assembly[] assembly = new[] { loadedAssembly };

                    // 合并程序集
                    scanAssemblies = scanAssemblies.Concat(assembly);
                    externalAssemblies = externalAssemblies.Concat(assembly);
                }
            }
            return (scanAssemblies, externalAssemblies);
        }


        /// <summary>
        /// 获取服务端口号
        /// </summary>
        /// <returns></returns>
        public static string GetPort()
        {
            return Configuration["urls"]?.Split(':').Last();
        }

        /// <summary>
        /// 获取环境名称
        /// </summary>
        /// <returns></returns>
        public static string EnvironmentName => HostEnvironment.EnvironmentName;



        /// <summary>
        /// 获取配置
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <param name="jsonKey">配置中对应的Key</param>
        /// <returns>TOptions</returns>
        public static TOptions GetConfig<TOptions>(string jsonKey, bool loadPostConfigure = false)
        {
            TOptions options = Configuration.GetSection(jsonKey).Get<TOptions>();

            // 加载默认选项配置
            if (loadPostConfigure && typeof(IConfigOptions).IsAssignableFrom(typeof(TOptions)))
            {
                MethodInfo postConfigure = typeof(TOptions).GetMethod("PostConfigure");
                if (postConfigure != null)
                {
                    options ??= Activator.CreateInstance<TOptions>();
                    postConfigure.Invoke(options, new object[] { options, Configuration });
                }
            }

            return options;
        }

        /// <summary>
        /// 获取选项
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <returns>TOptions</returns>
        public static TOptions GetOptions<TOptions>()
            where TOptions : class, new()
        {
            return ServiceLocator.Current.Create<IOptions<TOptions>>()?.Value;
        }

        /// <summary>
        /// 获取选项
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <returns>TOptions</returns>
        public static TOptions GetOptionsMonitor<TOptions>()
            where TOptions : class, new()
        {
            return ServiceLocator.Current.Create<IOptionsMonitor<TOptions>>()?.CurrentValue;
        }

        /// <summary>
        /// 获取选项
        /// </summary>
        /// <typeparam name="TOptions">强类型选项类</typeparam>
        /// <returns>TOptions</returns>
        public static TOptions GetOptionsSnapshot<TOptions>()
            where TOptions : class, new()
        {
            return ServiceLocator.Current.Create<IOptionsSnapshot<TOptions>>()?.Value;
        }

        /// <summary>
        /// 是否使用远程调用
        /// </summary>
        public static bool EnableRemote => GetOptions<ModuleSettingsOptions>().EnableRemote ?? false;
    }
}
